#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <asm/pgtable.h>
#include <linux/mm.h>
#include <linux/slab.h>

static int major = 0;
static struct class *hello_class;
static char *kernel_buf;
static int bufsiz = 1024 * 8;

#define MIN(a, b)   (a < b ? a : b)

static ssize_t mmap_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    int err;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    err = copy_to_user(buf, kernel_buf, MIN(bufsiz, size));
    return MIN(bufsiz, size);
}

static ssize_t mmap_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int err;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    err = copy_from_user(kernel_buf, buf, MIN(bufsiz, size));
	return MIN(1024, size);
}

static ssize_t mmap_close(struct inode *inode, struct file *file)
{
	printk("close driver\n");
	return 0;
}

static ssize_t mmap_open(struct inode *inode, struct file *file)
{
	printk("open driver\n");
	return 0;
}

static int drv_mmap(struct file *file, struct vm_area_struct *vma)
{
    /*获取物理地址*/
    unsigned long phy = virt_to_phys(kernel_buf);

    /*设置属性*/
    vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);

    /*mmap*/
    if(remap_pfn_range(vma, vma->vm_start, phy >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)){
        printk("mmap remap_pfn_range failed\n");
        return -ENOBUFS;
    }
    return 0;
}

struct file_operations hello_drv = {
	.owner	 = THIS_MODULE,
	.open	 = mmap_open,
	.release = mmap_close,
    .read    = mmap_read,
    .write   = mmap_write,
    .mmap    = drv_mmap,
};

static int __init mmap_drv_init(void)
{
	int err;

    kernel_buf = kmalloc(bufsiz, GFP_KERNEL);
    strcpy(kernel_buf, "old");

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "hello", &hello_drv);
	
	hello_class = class_create(THIS_MODULE, "hello_class");
	err = PTR_ERR(hello_class);
	if (IS_ERR(hello_class)){
		unregister_chrdev(major,"hello");
		return -1;
	}

	device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /*/dev/hello */

	return 0;
}

static void  __exit mmap_drv_exit(void)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(hello_class, MKDEV(major, 0));
	class_destroy(hello_class);
	unregister_chrdev(major,"hello");
    kfree(kernel_buf);
	
}

module_init(mmap_drv_init);
module_exit(mmap_drv_exit);

MODULE_LICENSE("GPL");
